home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.5) MIN_REFRESH_INTERVAL = 1 domain = 'system-config-printer' import locale pkgdata = '/usr/share/' + domain import sys import os import time import traceback import re import tempfile import httplib import thread if os.path.exists('system-config-printer.ui'): APPDIR = '.' else: APPDIR = '/usr/share/system-config-printer' SYSTEM_CONFIG_PRINTER_DIR = '/usr/share/system-config-printer' if os.path.exists(SYSTEM_CONFIG_PRINTER_DIR + '/debug.py'): sys.path.append(SYSTEM_CONFIG_PRINTER_DIR) from PyQt4.QtCore import * from PyQt4.QtGui import * from PyQt4 import uic import gettext gettext.textdomain(domain) def _(string): return unicode(gettext.gettext(string), 'utf-8') def translate(self, prop): '''reimplement method from uic to change it to use gettext''' if prop.get('notr', None) == 'true': return self._cstring(prop) elif prop.text is None: return '' text = prop.text.encode('UTF-8') return _(text) uic.properties.Properties._string = translate import cups cups.require('1.9.27') import config import cupshelpers from smburi import SMBURI from debug import * import dbus import dbus.mainloop.qt as dbus import dbus.service as dbus ellipsis = unichr(8230) try: try_CUPS_SERVER_REMOTE_ANY = cups.CUPS_SERVER_REMOTE_ANY except AttributeError: try_CUPS_SERVER_REMOTE_ANY = '_remote_any' def validDeviceURI(uri): '''Returns True is the provided URI is valid.''' if uri.find(':/') > 0: return True return False class GUI(QWidget): '''our main class is the main window''' printer_states = { cups.IPP_PRINTER_IDLE: _('Idle'), cups.IPP_PRINTER_PROCESSING: _('Processing'), cups.IPP_PRINTER_BUSY: _('Busy'), cups.IPP_PRINTER_STOPPED: _('Stopped') } def __init__(self, start_printer = None, change_ppd = False): QWidget.__init__(self) try: self.language = locale.getlocale(locale.LC_MESSAGES) self.encoding = locale.getlocale(locale.LC_CTYPE) except: nonfatalException() os.environ['LC_ALL'] = 'C' locale.setlocale(locale.LC_ALL, '') self.language = locale.getlocale(locale.LC_MESSAGES) self.encoding = locale.getlocale(locale.LC_CTYPE) self.printer = None self.conflicts = set() if not self.printer or self.printer.getServer(): pass self.connect_server = cups.getServer() self.connect_user = cups.getUser() self.password = '' self.passwd_retry = False self.widget_data_setting = { } self.changed = set() self.servers = set((self.connect_server,)) try: self.cups = cups.Connection() except RuntimeError: self.cups = None uic.loadUi(APPDIR + '/' + 'system-config-printer.ui', self) self.show() self.newPrinterGUI = np = NewPrinterGUI(self) self.setConnected() self.connect(self.mainlist, SIGNAL('itemSelectionChanged()'), self.on_tvMainList_cursor_changed) self.connect(self.mainlist, SIGNAL('currentItemChanged (QTreeWidgetItem*, QTreeWidgetItem*)'), self.on_tvMainList_changed) self.connect(self.chkServerBrowse, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.chkServerShare, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.chkServerShareAny, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.chkServerRemoteAdmin, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.chkServerAllowCancelAll, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.chkServerLogDebug, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.connect(self.btnNewClass, SIGNAL('clicked()'), self.on_new_class_activate) self.connect(self.btnNewPrinter, SIGNAL('clicked()'), self.on_new_printer_activate) self.connect(self.entPDescription, SIGNAL('textEdited(const QString&)'), self.on_printer_changed) self.connect(self.entPLocation, SIGNAL('textEdited(const QString&)'), self.on_printer_changed) self.connect(self.entPDevice, SIGNAL('textEdited(const QString&)'), self.on_printer_changed) self.connect(self.chkPEnabled, SIGNAL('stateChanged(int)'), self.on_printer_changed) self.connect(self.chkPAccepting, SIGNAL('stateChanged(int)'), self.on_printer_changed) self.connect(self.chkPShared, SIGNAL('stateChanged(int)'), self.on_printer_changed) self.connect(self.cmbPErrorPolicy, SIGNAL('currentIndexChanged(int)'), self.on_printer_changed) self.connect(self.cmbPOperationPolicy, SIGNAL('currentIndexChanged(int)'), self.on_printer_changed) self.connect(self.cmbPStartBanner, SIGNAL('currentIndexChanged(int)'), self.on_printer_changed) self.connect(self.cmbPEndBanner, SIGNAL('currentIndexChanged(int)'), self.on_printer_changed) try: self.populateList(start_printer, change_ppd) except cups.HTTPError: (s,) = None self.cups = None self.setConnected() self.populateList() self.show_HTTP_Error(s) self.mainlist.header().hide() self.btnNewPrinterNetwork.hide() self.newPrinterNetworkLabel.hide() self.btnNewPrinterSpecial.hide() self.newPrinterSpecialLabel.hide() self.btnNewPrinter.setText(_('New Printer')) self.btnPrinterPropertiesApply.setIcon(QIcon(APPDIR + '/dialog-ok-apply.png')) self.btnRevert.setIcon(QIcon(APPDIR + '/document-revert.png')) self.newPrinterLabel.hide() self.mainlist.setCurrentItem(self.settingsItem) self.lblPOptions.hide() self.lblPInstallOptions.hide() self.setWindowIcon(QIcon(APPDIR + '/printer-128.png')) def on_tvMainList_cursor_changed(self): if self.changed: return None items = self.mainlist.selectedItems() if len(items) < 1: return None item = items[0] type = str(item.text(1)) name = str(item.text(0)) self.mainListSelectedType = type self.mainListSelectedName = name item_selected = True if type == 'New': self.ntbkMain.setCurrentIndex(0) elif type == 'Settings': self.ntbkMain.setCurrentIndex(1) if self.cups: self.fillServerTab() else: self.setDataButtonState() item_selected = False elif type in ('Printer', 'Class'): try: self.fillPrinterTab(name) self.fillPrinterOptions() self.setDataButtonState() except RuntimeError: self.ntbkMain.setCurrentIndex(3) return None self.ntbkMain.setCurrentIndex(2) elif type == 'None': self.ntbkMain.setCurrentIndex(3) self.setDataButtonState() item_selected = False def printer_properties_response(self): (name, type) = self.getSelectedItem() if type in ('Printer', 'Class'): return self.save_printer(self.printer) elif type == 'Settings': return self.save_serversettings() def on_tvMainList_changed(self, new, old): '''about to change, offer to save''' if self.changed: answer = QMessageBox.question(self, 'Save Changes', 'Do you want to save changes?', QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save) if answer == QMessageBox.Save: self.printer_properties_response() elif answer == QMessageBox.Discard: self.changed = set() def busy(self, win = None): try: if not win: win = self win.setCursor(Qt.WaitCursor) QApplication.processEvents() except: nonfatalException() def ready(self, win = None): try: if not win: win = self win.setCursor(Qt.ArrowCursor) QApplication.processEvents() except: nonfatalException() def setConnected(self): connected = bool(self.cups) host = cups.getServer() if host[0] == '/': host = 'localhost' self.setWindowTitle(_('Printer configuration - %s') % host) if connected: status_msg = _('Connected to %s') % host else: status_msg = _('Not connected') for widget in (self.chkServerBrowse, self.chkServerShare, self.chkServerRemoteAdmin, self.chkServerAllowCancelAll, self.chkServerLogDebug): widget.setEnabled(connected) sharing = self.chkServerShare.isChecked() self.chkServerShareAny.setEnabled(sharing) try: del self.server_settings except: pass def populateList(self, start_printer = None, change_ppd = False): old_name = '' old_type = '' select_path = None if self.cups: try: self.printers = cupshelpers.getPrinters(self.cups) try: self.default_printer = self.cups.getDefault() except AttributeError: dests = self.cups.getDests() if dests.has_key((None, None)): self.default_printer = dests[(None, None)].name else: self.default_printer = None except: dests.has_key((None, None)) except cups.IPPError: (e, m) = None self.show_IPP_Error(e, m) self.printers = { } self.default_printer = None except: None<EXCEPTION MATCH>cups.IPPError None<EXCEPTION MATCH>cups.IPPError self.printers = { } self.default_printer = None local_printers = [] local_classes = [] remote_printers = [] remote_classes = [] for name, printer in self.printers.iteritems(): if printer.default: self.default_printer = name self.servers.add(printer.getServer()) if printer.remote: if printer.is_class: remote_classes.append(name) else: remote_printers.append(name) printer.is_class if printer.is_class: local_classes.append(name) continue local_printers.append(name) local_printers.sort() local_classes.sort() remote_printers.sort() remote_classes.sort() if old_name != '' and old_name not in local_printers and old_name not in local_classes and old_name not in remote_printers and old_name not in remote_classes: old_name = '' if self.default_printer != None and start_printer == None and old_name == '': start_printer = self.default_printer if not start_printer: start_printer = old_name expanded = { '_printers': True, '_classes': True, '_remote_printers': True, '_remote_classes': True } self.mainlist.clear() QTreeWidgetItem(self.mainlist, [ 'New Printer', 'New']) self.settingsItem = QTreeWidgetItem(self.mainlist, [ 'Server Settings', 'Settings']) for printers, text, name in ((local_printers, _('Local Printers'), '_printers'), (local_classes, _('Local Classes'), '_classes'), (remote_printers, _('Remote Printers'), '_remote_printers'), (remote_classes, _('Remote Classes'), '_remote_classes')): if not printers: continue rootTreeItem = QTreeWidgetItem(self.mainlist, [ text, name]) for printer_name in printers: if start_printer == None: start_printer = printer_name treeItem = QTreeWidgetItem(rootTreeItem, [ printer_name, 'Printer']) if printer_name == start_printer: treeItem.setSelected(True) expanded[name] = True continue if expanded[name]: rootTreeItem.setExpanded(True) continue self.on_tvMainList_cursor_changed() self.setDataButtonState() def on_printer_changed(self, text): widget = self.sender() if not widget: return None if isinstance(widget, QCheckBox): value = widget.isChecked() elif isinstance(widget, QLineEdit): value = unicode(widget.text()) elif isinstance(widget, QRadioButton): value = widget.isChecked() elif isinstance(widget, QComboBox): value = unicode(widget.currentText()) else: raise ValueError, 'Widget type not supported (yet)' p = self.printer old_values = { self.entPDescription: p.info, self.entPLocation: p.location, self.entPDevice: p.device_uri, self.chkPEnabled: p.enabled, self.chkPAccepting: not (p.rejecting), self.chkPShared: p.is_shared, self.cmbPStartBanner: p.job_sheet_start, self.cmbPEndBanner: p.job_sheet_end, self.cmbPErrorPolicy: p.error_policy, self.cmbPOperationPolicy: p.op_policy } old_value = old_values[widget] if old_value == value: self.changed.discard(widget) else: self.changed.add(widget) self.setDataButtonState() def setDataButtonState(self): try: if self.ppd and not bool(self.changed) and self.printer.enabled: pass possible = not (self.printer.rejecting) self.btnPrintTestPage.setEnabled(possible) commands = self.printer.type & cups.CUPS_PRINTER_COMMANDS != 0 if commands: pass self.btnSelfTest.setEnabled(possible) if commands: pass self.btnCleanHeads.setEnabled(possible) except: debugprint('exception in setDataButtonState') installablebold = False optionsbold = False if self.conflicts: debugprint('Conflicts detected') self.btnConflict.show() for option in self.conflicts: if option.tab_label == self.lblPInstallOptions: installablebold = True continue optionsbold = True else: self.btnConflict.hide() installabletext = _('Installable Options') optionstext = _('Printer Options') if installablebold: installabletext = '<b>%s</b>' % installabletext if optionsbold: optionstext = '<b>%s</b>' % optionstext self.lblPInstallOptions.setText(installabletext) self.lblPOptions.setText(optionstext) self.btnPrinterPropertiesApply.setEnabled(len(self.changed) > 0) self.btnRevert.setEnabled(len(self.changed) > 0) def save_printer(self, printer, saveall = False): class_deleted = False name = printer.name try: if not (printer.is_class) and self.ppd: self.getPrinterSettings() if self.ppd.nondefaultsMarked() or saveall: self.passwd_retry = False self.cups.addPrinter(name, ppd = self.ppd) if printer.is_class: pass location = unicode(self.entPLocation.text()) info = unicode(self.entPDescription.text()) device_uri = unicode(self.entPDevice.text()) if device_uri.find(ellipsis) != -1: device_uri = printer.device_uri enabled = self.chkPEnabled.isChecked() accepting = self.chkPAccepting.isChecked() shared = self.chkPShared.isChecked() if info != printer.info or saveall: self.passwd_retry = False self.cups.setPrinterInfo(name, info) if location != printer.location or saveall: self.passwd_retry = False self.cups.setPrinterLocation(name, location) if not (printer.is_class): if device_uri != printer.device_uri or saveall: self.passwd_retry = False self.cups.setPrinterDevice(name, device_uri) if enabled != printer.enabled or saveall: self.passwd_retry = False self.printer.setEnabled(enabled) if accepting == printer.rejecting or saveall: self.passwd_retry = False self.printer.setAccepting(accepting) if shared != printer.is_shared or saveall: self.passwd_retry = False self.printer.setShared(shared) job_sheet_start = unicode(self.cmbPStartBanner.currentText()) job_sheet_end = unicode(self.cmbPEndBanner.currentText()) error_policy = unicode(self.cmbPErrorPolicy.currentText()) op_policy = unicode(self.cmbPOperationPolicy.currentText()) if job_sheet_start != printer.job_sheet_start or job_sheet_end != printer.job_sheet_end or saveall: self.passwd_retry = False printer.setJobSheets(job_sheet_start, job_sheet_end) if error_policy != printer.error_policy or saveall: self.passwd_retry = False printer.setErrorPolicy(error_policy) if op_policy != printer.op_policy or saveall: self.passwd_retry = False printer.setOperationPolicy(op_policy) except cups.IPPError: (e, s) = None self.show_IPP_Error(e, s) return True self.changed = set() if not self.__dict__.has_key('server_settings'): try: self.server_settings = self.cups.adminGetServerSettings() nonfatalException() if class_deleted: self.populateList() else: printers = cupshelpers.getPrinters(self.cups) this_printer = { name: printers[name] } self.printers.update(this_printer) return False def getPrinterSettings(self): for option in self.options.itervalues(): option.writeback() def on_btnPrintTestPage_clicked(self): if self.test_button_cancels: jobs = self.printer.testsQueued() for job in jobs: debugprint('Canceling job %s' % job) try: self.cups.cancelJob(job) continue except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) continue self.setTestButton(self.printer) return None try: custom_testpage = None opt = self.ppd.findOption('PageSize') if opt: custom_testpage = os.path.join(pkgdata, 'testpage-%s.ps' % opt.defchoice.lower()) if custom_testpage and os.path.exists(custom_testpage): debugprint('Printing custom test page ' + custom_testpage) job_id = self.cups.printTestPage(self.printer.name, file = custom_testpage) else: debugprint('Printing default test page') job_id = self.cups.printTestPage(self.printer.name) self.setTestButton(self.printer) QMessageBox.information(self, _('Submitted'), _('Test page submitted as job %d') % job_id) except cups.IPPError: (e, msg) = None if e == cups.IPP_NOT_AUTHORIZED and self.connect_server != 'localhost' and self.connect_server[0] != '/': self.lblError.set_markup('<span weight="bold" size="larger">' + _('Not possible') + '</span>\n\n' + _('The remote server did not accept the print job, most likely because the printer is not shared.')) self.ErrorDialog.set_transient_for(self.MainWindow) self.ErrorDialog.run() self.ErrorDialog.hide() else: self.show_IPP_Error(e, msg) except: self.connect_server[0] != '/' on_btnPrintTestPage_clicked = pyqtSignature('')(on_btnPrintTestPage_clicked) def maintenance_command(self, command): (tmpfd, tmpfname) = tempfile.mkstemp() os.write(tmpfd, '#CUPS-COMMAND\n%s\n' % command) os.close(tmpfd) try: format = 'application/vnd.cups-command' job_id = self.cups.printTestPage(self.printer.name, format = format, file = tmpfname, user = self.connect_user) self.lblInfo.set_markup('<span weight="bold" size="larger">' + _('Submitted') + '</span>\n\n' + _('Maintenance command submitted as job %d') % job_id) self.InfoDialog.set_transient_for(self.MainWindow) self.InfoDialog.run() self.InfoDialog.hide() except cups.IPPError: (e, msg) = None if e == cups.IPP_NOT_AUTHORIZED and self.printer.name != 'localhost': self.lblError.set_markup('<span weight="bold" size="larger">' + _('Not possible') + '</span>\n\n' + _('THe remote server did not accept the print job, most likely because the printer is not shared.')) self.ErrorDialog.set_transient_for(self.MainWindow) self.ErrorDialog.run() self.ErrorDialog.hide() else: self.show_IPP_Error(e, msg) except: self.printer.name != 'localhost' def on_btnSelfTest_clicked(self): self.maintenance_command('PrintSelfTestPage') on_btnSelfTest_clicked = pyqtSignature('')(on_btnSelfTest_clicked) def on_btnCleanHeads_clicked(self): self.maintenance_command('Clean all') on_btnCleanHeads_clicked = pyqtSignature('')(on_btnCleanHeads_clicked) def fillComboBox(self, combobox, values, value): combobox.clear() for nr, val in enumerate(values): combobox.addItem(val) if val == value: combobox.setCurrentIndex(nr) continue def fillPrinterTab(self, name): self.changed = set() self.options = { } self.conflicts = set() printer = self.printers[name] self.printer = printer printer.getAttributes() editable = not (self.printer.discovered) editablePPD = not (self.printer.remote) try: self.ppd = printer.getPPD() except cups.IPPError: (e, m) = None self.show_IPP_Error(e, m) self.ppd = False except RuntimeError: debugprint('Error!') raise for widget in (self.entPDescription, self.entPLocation, self.entPDevice): widget.setReadOnly(not editable) for widget in (self.btnSelectDevice, self.btnChangePPD): widget.setEnabled(editable) self.entPDescription.setText(printer.info) self.entPLocation.setText(printer.location) uri = printer.device_uri if uri.startswith('smb://'): (group, host, share, user, password) = SMBURI(uri = uri[6:]).separate() if password: uri = 'smb://' if len(user) or len(password): uri += ellipsis uri += SMBURI(group = group, host = host, share = share).get_uri() self.entPDevice.setEnabled(False) else: self.entPDevice.setEnabled(True) self.entPDevice.setText(uri) self.changed.discard(self.entPDevice) for widget in (self.lblPMakeModel2, self.lblPMakeModel, self.btnChangePPD, self.lblPDevice2, self.entPDevice, self.btnSelectDevice): if printer.is_class: widget.hide() continue widget.show() self.btnPMakeDefault.setEnabled(not (printer.default)) if printer.default: self.lblPDefault.setText(_('This is the default printer')) elif self.default_printer: self.lblPDefault.setText(self.default_printer) else: self.lblPDefault.setText(_('No default printer set.')) self.setTestButton(printer) self.chkPEnabled.setChecked(printer.enabled) self.chkPAccepting.setChecked(not (printer.rejecting)) self.chkPShared.setChecked(printer.is_shared) try: if printer.is_shared: flag = cups.CUPS_SERVER_SHARE_PRINTERS publishing = int(self.server_settings[flag]) if publishing: self.lblNotPublished.hide() else: self.lblNotPublished.show() else: self.lblNotPublished.hide() except: self.lblNotPublished.hide() self.cmbPStartBanner.setEnabled(editable) self.cmbPEndBanner.setEnabled(editable) self.cmbPErrorPolicy.setEnabled(editable) self.cmbPOperationPolicy.setEnabled(editable) self.changed = set() self.updatePrinterProperties() self.setDataButtonState() def fillPrinterOptions(self): pass def on_btnDelete_clicked(self): (name, type) = self.getSelectedItem() print name if type == 'Printer': message_format = _('Really delete printer %s?') else: message_format = _('Really delete class %s?') cancel = QMessageBox.question(self, '', message_format % name, _('&Yes'), _('&No'), QString(), 0, 1) if cancel: return None try: self.cups.deletePrinter(name) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) self.changed = set() self.populateList() self.mainlist.setCurrentItem(self.mainlist.itemAt(0, 0)) on_btnDelete_clicked = pyqtSignature('')(on_btnDelete_clicked) def on_btnPMakeDefault_clicked(self): try: self.cups.setDefault(self.printer.name) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None (tmpfd, tmpfname) = tempfile.mkstemp() success = False try: resource = '/admin/conf/lpoptions' self.cups.getFile(resource, tmpfname) except cups.HTTPError: (s,) = None try: os.remove(tmpfname) except OSError: pass if s != cups.HTTP_NOT_FOUND: self.show_HTTP_Error(s) return None except: s != cups.HTTP_NOT_FOUND if success: lines = file(tmpfname).readlines() changed = False i = 0 for line in lines: if line.startswith('Default '): name = line.split(' ')[1] if name != self.printer.name: lines[i] = 'Dest ' + line[8:] changed = True i += 1 if changed: file(tmpfname, 'w').writelines(lines) try: self.cups.putFile(resource, tmpfname) except cups.HTTPError: (s,) = None os.remove(tmpfname) print s self.show_HTTP_Error(s) return None self.reconnect() try: os.remove(tmpfname) except OSError: pass try: self.populateList() except cups.HTTPError: (s,) = None self.cups = None self.setConnected() self.populateList() self.show_HTTP_Error(s) on_btnPMakeDefault_clicked = pyqtSignature('')(on_btnPMakeDefault_clicked) def fillServerTab(self): self.changed = set() try: self.server_settings = self.cups.adminGetServerSettings() except cups.IPPError: (e, m) = None self.show_IPP_Error(e, m) self.tvMainList.get_selection().unselect_all() self.on_tvMainList_cursor_changed(self.tvMainList) return None for widget, setting in [ (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS), (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS), (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY), (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN), (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY), (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING)]: self.widget_data_setting[widget] = setting self.disconnect(widget, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) if self.server_settings.has_key(setting): widget.setChecked(int(self.server_settings[setting])) widget.setEnabled(True) else: widget.setChecked(False) widget.setEnabled(False) self.connect(widget, SIGNAL('stateChanged(int)'), self.on_server_widget_changed) self.setDataButtonState() self.on_server_changed(self.chkServerShare) def on_server_widget_changed(self, value): self.on_server_changed(self.sender()) def on_server_changed(self, widget): setting = self.widget_data_setting[widget] if self.server_settings.has_key(setting): if str(int(widget.isChecked())) == self.server_settings[setting]: self.changed.discard(widget) else: self.changed.add(widget) sharing = self.chkServerShare.isChecked() if sharing: pass self.chkServerShareAny.setEnabled(self.server_settings.has_key(try_CUPS_SERVER_REMOTE_ANY)) self.setDataButtonState() def save_serversettings(self): setting_dict = self.server_settings.copy() for widget, setting in [ (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS), (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS), (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY), (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN), (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY), (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING)]: if not self.server_settings.has_key(setting): continue setting_dict[setting] = str(int(widget.isChecked())) try: self.cups.adminSetServerSettings(setting_dict) except cups.IPPError: (e, m) = None self.show_IPP_Error(e, m) return True except RuntimeError: s = None self.show_IPP_Error(None, s) return True self.changed = set() self.setDataButtonState() time.sleep(1) self.reconnect() try: self.fillServerTab() except: nonfatalException() def on_new_printer_activate(self): self.busy() self.newPrinterGUI.init('printer') self.ready() def on_new_class_activate(self): self.newPrinterGUI.init('class') def on_btnSelectDevice_clicked(self): self.newPrinterGUI.init('device') on_btnSelectDevice_clicked = pyqtSignature('')(on_btnSelectDevice_clicked) def on_btnChangePPD_clicked(self): self.busy(self) self.newPrinterGUI.init('ppd') self.ready(self) on_btnChangePPD_clicked = pyqtSignature('')(on_btnChangePPD_clicked) def checkNPName(self, name): if not name: return False name = name.lower() for printer in self.printers.values(): if not (printer.discovered) and printer.name.lower() == name: return False continue return True def makeNameUnique(self, name): '''Make a suggested queue name valid and unique.''' name = name.replace(' ', '_') name = name.replace('/', '_') name = name.replace('#', '_') if not self.checkNPName(name): suffix = 2 while not self.checkNPName(name + str(suffix)): suffix += 1 if suffix == 100: break continue name += str(suffix) return name def on_btnRevert_clicked(self): self.changed = set() self.on_tvMainList_cursor_changed() on_btnRevert_clicked = pyqtSignature('')(on_btnRevert_clicked) def on_btnPrinterPropertiesApply_clicked(self): err = self.printer_properties_response() if not err: self.populateList() else: nonfatalException() on_btnPrinterPropertiesApply_clicked = pyqtSignature('')(on_btnPrinterPropertiesApply_clicked) def show_IPP_Error(self, exception, message): if exception == cups.IPP_NOT_AUTHORIZED: QMessageBox.critical(self, _('Not authorized'), _('The password may be incorrect.')) else: QMessageBox.critical(self, _('CUPS server error'), _("There was an error during the CUPS operation: '%s'.") % message) def show_HTTP_Error(self, status): if status == cups.HTTP_UNAUTHORIZED or status == cups.HTTP_FORBIDDEN: QMessageBox.critical(self, _('Not authorized'), _('The password may be incorrect, or the server may be configured to deny remote administration.')) elif status == cups.HTTP_BAD_REQUEST: msg = _('Bad request') elif status == cups.HTTP_NOT_FOUND: msg = _('Not found') elif status == cups.HTTP_REQUEST_TIMEOUT: msg = _('Request timeout') elif status == cups.HTTP_UPGRADE_REQUIRED: msg = _('Upgrade required') elif status == cups.HTTP_SERVER_ERROR: msg = _('Server error') elif status == -1: msg = _('Not connected') else: msg = _('status %d') % status error_text = ('<span weight="bold" size="larger">' + _('CUPS server error') + '</span>\n\n' + _('There was an HTTP error: %s.')) % msg QMessageBox.critical(self, _('CUPS server error'), _('There was an HTTP error: %s.') % msg) def getSelectedItem(self): return (str(self.mainListSelectedName).decode('utf-8'), str(self.mainListSelectedType)) def reconnect(self): '''Reconnect to CUPS after the server has reloaded.''' self.cups = None self.setConnected() cups.setServer(self.connect_server) cups.setUser(self.connect_user) attempt = 1 while attempt <= 5: try: self.cups = cups.Connection() continue except RuntimeError: time.sleep(1) attempt += 1 continue None<EXCEPTION MATCH>RuntimeError self.setConnected() self.passwd_retry = False def updatePrinterProperties(self): debugprint('update printer properties') printer = self.printer self.lblPMakeModel.setText(printer.make_and_model) state = self.printer_states.get(printer.state, _('Unknown')) reason = printer.other_attributes.get('printer-state-message', '') if len(reason) > 0: state += ' - ' + reason self.lblPState.setText(state) if len(self.changed) == 0: debugprint('no changes yet: full printer properties update') self.chkPEnabled.setEnabled(printer.enabled) self.chkPAccepting.setEnabled(not (printer.rejecting)) self.chkPShared.setEnabled(printer.is_shared) (self.fillComboBox(self.cmbPStartBanner, printer.job_sheets_supported, printer.job_sheet_start),) self.fillComboBox(self.cmbPEndBanner, printer.job_sheets_supported, printer.job_sheet_end) self.fillComboBox(self.cmbPErrorPolicy, printer.error_policy_supported, printer.error_policy) self.fillComboBox(self.cmbPOperationPolicy, printer.op_policy_supported, printer.op_policy) def setTestButton(self, printer): if printer.testsQueued(): self.test_button_cancels = True self.btnPrintTestPage.setText(_('Cancel Tests')) self.btnPrintTestPage.setEnabled(True) else: self.test_button_cancels = False self.btnPrintTestPage.setText(_('Print Test Page')) self.setDataButtonState() def getCurrentClassMembers(self, listwidget): count = listwidget.count() result = [] for i in range(count): result.append(listwidget.item(i).text()) result.sort() return result def moveClassMembers(self, treeview_from, treeview_to): rows = treeview_from.selectedItems() for row in rows: treeview_from.takeItem(treeview_from.row(row)) treeview_to.addItem(row) def cupsPasswdCallback(self, querystring): return '' if self.passwd_retry or len(self.password) == 0: waiting = self.WaitWindow.get_property('visible') if waiting: self.WaitWindow.hide() self.lblPasswordPrompt.set_label(self.prompt_primary + querystring) self.PasswordDialog.set_transient_for(self.MainWindow) self.entPasswd.grab_focus() result = self.PasswordDialog.run() self.PasswordDialog.hide() if waiting: self.WaitWindow.show() while gtk.events_pending(): gtk.main_iteration() if result == gtk.RESPONSE_OK: self.password = self.entPasswd.get_text() else: self.password = '' self.passwd_retry = False else: self.passwd_retry = True return self.password class NewPrinterGUI(QDialog): new_printer_device_tabs = { 'parallel': 0, 'usb': 0, 'hal': 0, 'beh': 0, 'hp': 0, 'hpfax': 0, 'socket': 2, 'ipp': 3, 'http': 3, 'lpd': 4, 'scsi': 5, 'serial': 6, 'smb': 7 } ntbkNewPrinterPages = { 'name': 0, 'device': 1, 'make': 2, 'model': 3, 'class-members': 4, 'downloadable': -1 } def __init__(self, mainapp): QDialog.__init__(self, mainapp) self.mainapp = mainapp self.language = mainapp.language self.dialog_mode = '' self.WaitWindow = QMessageBox(self.mainapp) self.WaitWindow.setStandardButtons(QMessageBox.NoButton) uic.loadUi(APPDIR + '/' + 'new-printer.ui', self) self.connect(self.tvNPDevices, SIGNAL('itemSelectionChanged()'), self.on_tvNPDevices_cursor_changed) self.connect(self.tvNPMakes, SIGNAL('itemSelectionChanged()'), self.on_tvNPMakes_cursor_changed) self.connect(self.tvNPModels, SIGNAL('itemSelectionChanged()'), self.on_tvNPModels_cursor_changed) self.connect(self.entNPTDevice, SIGNAL('textEdited(const QString&)'), self.on_entNPTDevice_changed) self.connect(self.entSMBURI, SIGNAL('textEdited(const QString&)'), self.on_entSMBURI_changed) self.rbtnSMBAuthPrompt.setChecked(True) self.on_rbtnSMBAuthSet_toggled(False) self.connect(self.rbtnSMBAuthSet, SIGNAL('toggled(bool)'), self.on_rbtnSMBAuthSet_toggled) self.rbtnNPFoomatic.setChecked(True) self.connect(self.rbtnNPFoomatic, SIGNAL('toggled(bool)'), self.on_rbtnNPFoomatic_toggled) self.options = { } self.changed = set() self.conflicts = set() self.ppd = None self.ppds_lock = thread.allocate_lock() self.devices_lock = thread.allocate_lock() self.smb_lock = thread.allocate_lock() self.ipp_lock = thread.allocate_lock() self.drivers_lock = thread.allocate_lock() if not config.DOWNLOADABLE_DRIVER_SUPPORT: self.rbtnNPDownloadableDriverSearch.setEnabled(False) self.downloadableDriverSearchFrame.hide() self.btnSMBBrowse.hide() self.btnSMBVerify.hide() self.btnIPPFindQueue.hide() self.btnIPPVerify.hide() self.btnNPTLpdProbe.hide() def option_changed(self, option): if option.is_changed(): self.changed.add(option) else: self.changed.discard(option) if option.conflicts: self.conflicts.add(option) else: self.conflicts.discard(option) self.setDataButtonState() def setDataButtonState(self): self.btnNPForward.setEnabled(not bool(self.conflicts)) def init(self, dialog_mode): self.dialog_mode = dialog_mode self.options = { } self.changed = set() self.conflicts = set() if self.dialog_mode == 'printer': self.setWindowTitle(_('New Printer')) self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages['device']) self.fillDeviceTab() self.on_rbtnNPFoomatic_toggled() self.new_printer_PPDs_loaded = False self.queryPPDs() elif self.dialog_mode == 'class': self.setWindowTitle(_('New Class')) self.fillNewClassMembers() self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages['name']) elif self.dialog_mode == 'device': self.setWindowTitle(_('Change Device URI')) self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages['device']) self.queryDevices() self.loadPPDs() self.fillDeviceTab(self.mainapp.printer.device_uri) self.new_printer_PPDs_loaded = False self.queryPPDs() elif self.dialog_mode == 'ppd': self.setWindowTitle(_('Change Driver')) self.ntbkNewPrinter.setCurrentIndex(2) self.on_rbtnNPFoomatic_toggled() self.auto_model = '' ppd = self.mainapp.ppd if ppd: attr = ppd.findAttr('Manufacturer') if attr: self.auto_make = attr.value else: self.auto_make = '' attr = ppd.findAttr('ModelName') if not attr: attr = ppd.findAttr('ShortNickName') if not attr: attr = ppd.findAttr('NickName') if attr: pass None if attr.value.startswith(self.auto_make) else None<EXCEPTION MATCH>IndexError self.auto_model = '' else: self.auto_make = 'Raw' self.auto_model = 'Queue' self.loadPPDs() self.fillMakeList() if self.dialog_mode in ('printer', 'class'): self.entNPName.setText(self.mainapp.makeNameUnique(self.dialog_mode)) for widget in [ self.entNPLocation, self.entNPDescription]: widget.setText('') try: p = os.popen('/bin/hostname', 'r') hostname = p.read().strip() p.close() self.entNPLocation.setText(hostname) nonfatalException() self.entNPTDirectJetPort.setText('9100') self.setNPButtons() self.exec_() def queryPPDs(self): debugprint('queryPPDs') if not self.ppds_lock.acquire(0): debugprint('queryPPDs: in progress') return None debugprint('Lock acquired for PPDs thread') thread.start_new_thread(self.getPPDs_thread, (self.language[0],)) debugprint('PPDs thread started') def getPPDs_thread(self, language): try: debugprint('Connecting (PPDs)') cups.setServer(self.mainapp.connect_server) cups.setUser(self.mainapp.connect_user) cups.setPasswordCB(self.mainapp.cupsPasswdCallback) c = cups.Connection() debugprint('Fetching PPDs') ppds_dict = c.getPPDs() self.ppds_result = cupshelpers.ppds.PPDs(ppds_dict, language = language) debugprint('Closing connection (PPDs)') del c except cups.IPPError: (e, msg) = None self.ppds_result = cups.IPPError(e, msg) except: nonfatalException() self.ppds_result = { } debugprint('Releasing PPDs lock') self.ppds_lock.release() def fetchPPDs(self, parent = None): debugprint('fetchPPDs') self.queryPPDs() time.sleep(0.1) waiting = False while self.ppds_lock.locked(): if not waiting: waiting = True self.WaitWindow.setText('<b>' + _('Searching') + '</b><br /><br />' + _('Searching for drivers')) self.WaitWindow.show() QApplication.processEvents() time.sleep(0.1) if waiting: self.WaitWindow.hide() debugprint('Got PPDs') result = self.ppds_result if isinstance(result, cups.IPPError): raise result return result def loadPPDs(self, parent = None): try: return self.ppds except: self.ppds = self.fetchPPDs(parent = parent) return self.ppds def dropPPDs(self): try: del self.ppds except: pass def fillNewClassMembers(self): self.tvNCMembers.clear() self.tvNCNotMembers.clear() for printer in self.mainapp.printers.itervalues(): self.tvNCNotMembers.addItem(printer.name) def on_btnNCAddMember_clicked(self): self.mainapp.moveClassMembers(self.tvNCNotMembers, self.tvNCMembers) self.btnNPApply.setEnabled(bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers))) on_btnNCAddMember_clicked = pyqtSignature('')(on_btnNCAddMember_clicked) def on_btnNCDelMember_clicked(self): self.mainapp.moveClassMembers(self.tvNCMembers, self.tvNCNotMembers) self.btnNPApply.setEnabled(bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers))) on_btnNCDelMember_clicked = pyqtSignature('')(on_btnNCDelMember_clicked) def on_btnNPBack_clicked(self): self.nextNPTab(-1) on_btnNPBack_clicked = pyqtSignature('')(on_btnNPBack_clicked) def on_btnNPForward_clicked(self): self.nextNPTab() on_btnNPForward_clicked = pyqtSignature('')(on_btnNPForward_clicked) def nextNPTab(self, step = 1): page_nr = self.ntbkNewPrinter.currentIndex() if self.dialog_mode == 'class': order = [ self.ntbkNewPrinterPages['name'], self.ntbkNewPrinterPages['class-members']] elif self.dialog_mode == 'printer': if page_nr == 1: name = 'printer' try: if self.device.id: name = self.device.id_dict['MDL'] name = self.mainapp.makeNameUnique(name) self.entNPName.setText(name) except: nonfatalException() (self.auto_make, self.auto_model) = (None, None) self.device.uri = self.getDeviceURI() if self.device.type in ('socket', 'lpd', 'ipp', 'bluetooth'): host = self.getNetworkPrinterMakeModel(self.device) faxuri = None if host: faxuri = self.get_hplip_uri_for_network_printer(host, 'fax') if faxuri: dialog = gtk.Dialog(self.device.info, self.NewPrinterWindow, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (_('Printer'), 1, _('Fax'), 2)) label = gtk.Label(_('This printer supports both printing and sending faxes. Which functionality should be used for this queue?')) dialog.vbox.pack_start(label, True, True, 0) label.show() queue_type = dialog.run() dialog.destroy() if queue_type == 2: self.device.id_dict = self.get_hpfax_device_id(faxuri) self.device.uri = faxuri self.auto_make = self.device.id_dict['MFG'] self.auto_model = self.device.id_dict['MDL'] self.device.id = 'MFG:' + self.auto_make + ';MDL:' + self.auto_model + ';DES:' + self.device.id_dict['DES'] + ';' uri = self.device.uri if uri and uri.startswith('smb://'): uri = SMBURI(uri = uri[6:]).sanitize_uri() self.remotecupsqueue = None res = re.search('ipp://(\\S+(:\\d+|))/printers/(\\S+)', uri) if res: resg = res.groups() try: conn = httplib.HTTPConnection(resg[0]) conn.request('GET', '/printers/%s.ppd' % resg[2]) resp = conn.getresponse() if resp.status == 200: self.remotecupsqueue = resg[2] except: debugprint('exception in getting remotecupsqueue') oldserver = cups.getServer() oldport = cups.getPort() try: cups.setServer(resg[0]) if len(resg[1]) > 0: cups.setPort(int(resg[1])) else: cups.setPort(631) c = cups.Connection() r = [ 'printer-info', 'printer-location'] attrs = c.getPrinterAttributes(uri = uri, requested_attributes = r) info = attrs.get('printer-info', '') location = attrs.get('printer-location', '') if len(info) > 0: self.entNPDescription.setText(info) if len(location) > 0: self.entNPLocation.setText(location) except: nonfatalException() cups.setServer(oldserver) cups.setPort(oldport) if not (self.remotecupsqueue) and not (self.new_printer_PPDs_loaded): try: self.loadPPDs(self) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None except: self.ready(self) return None self.new_printer_PPDs_loaded = True ppdname = None try: if self.remotecupsqueue: ppdname = 'raw' self.ppd = ppdname name = self.remotecupsqueue name = self.mainapp.makeNameUnique(name) self.entNPName.setText(name) elif self.device.id: id_dict = self.device.id_dict (status, ppdname) = self.ppds.getPPDNameFromDeviceID(id_dict['MFG'], id_dict['MDL'], id_dict['DES'], id_dict['CMD'], self.device.uri) else: (status, ppdname) = self.ppds.getPPDNameFromDeviceID('Generic', 'Printer', 'Generic Printer', [], self.device.uri) if ppdname and not (self.remotecupsqueue): ppddict = self.ppds.getInfoFromPPDName(ppdname) make_model = ppddict['ppd-make-and-model'] (make, model) = cupshelpers.ppds.ppdMakeModelSplit(make_model) self.auto_make = make self.auto_model = model except: nonfatalException() if not self.remotecupsqueue: self.fillMakeList() elif page_nr == 3: if not self.device.id: try: items = self.tvNPModels.selectedItems() name = unicode(items[0].text()) name = self.mainapp.makeNameUnique(name) self.entNPName.setText(name) nonfatalException() if self.remotecupsqueue: order = [ 1, 0] elif self.rbtnNPFoomatic.isChecked(): order = [ 1, 2, 3, 6, 0] elif self.rbtnNPPPD.isChecked(): order = [ 1, 2, 6, 0] else: order = [ 1, 2, 7, 6, 0] elif self.dialog_mode == 'device': order = [ 1] elif self.dialog_mode == 'ppd': self.rbtnChangePPDasIs.setChecked(True) if self.rbtnNPFoomatic.isChecked(): order = [ 2, 3, 5, 6] elif self.rbtnNPPPD.isChecked(): order = [ 2, 5, 6] else: order = [ 2, 7, 5, 6] next_page_nr = order[order.index(page_nr) + step] if next_page_nr == 6 and step > 0: self.ppd = self.getNPPPD() next_page_nr = order[order.index(next_page_nr) + 1] self.installable_options = False if next_page_nr == 6 and not (self.installable_options) and step < 0: next_page_nr = order[order.index(next_page_nr) - 1] if next_page_nr == 7: if self.drivers_lock.locked(): self.lblWait.set_markup('<span weight="bold" size="larger">' + _('Searching') + '</span>\n\n' + _('Searching for drivers')) self.WaitWindow.set_transient_for(self.NewPrinterWindow) self.WaitWindow.show() self.busy(self) while self.drivers_lock.locked(): while gtk.events_pending(): gtk.main_iteration() time.sleep(0.1) self.ready(self.NewPrinterWindow) self.WaitWindow.hide() self.fillDownloadableDrivers() self.ntbkNewPrinter.setCurrentIndex(next_page_nr) self.setNPButtons() def setNPButtons(self): nr = self.ntbkNewPrinter.currentIndex() if self.dialog_mode == 'device': self.btnNPBack.hide() self.btnNPForward.hide() self.btnNPApply.show() uri = self.getDeviceURI() self.btnNPApply.setEnabled(validDeviceURI(uri)) return None if self.dialog_mode == 'ppd': if nr == 5: if not self.installable_options: debugprint('No installable options') self.btnNPForward.hide() self.btnNPApply.show() else: self.btnNPForward.show() self.btnNPApply.hide() return None elif nr == 6: self.btnNPForward.hide() self.btnNPApply.show() return None else: self.btnNPForward.show() self.btnNPApply.hide() if nr == 2: self.btnNPBack.hide() self.btnNPForward.show() downloadable_selected = False if self.rbtnNPDownloadableDriverSearch.isChecked(): combobox = self.cmbNPDownloadableDriverFoundPrinters iter = combobox.get_active_iter() if iter and combobox.get_model().get_value(iter, 1): downloadable_selected = True if not self.rbtnNPFoomatic.isChecked() and self.filechooserPPD.get_filename(): pass self.btnNPForward.setEnabled(bool(downloadable_selected)) return None else: self.btnNPBack.show() if nr == 1: valid = False try: uri = self.getDeviceURI() valid = validDeviceURI(uri) except: pass self.btnNPForward.setEnabled(valid) self.btnNPBack.hide() else: self.btnNPBack.show() self.btnNPForward.show() self.btnNPApply.hide() if nr == 0: self.btnNPBack.show() if self.dialog_mode == 'class': self.btnNPForward.setEnabled(True) if self.dialog_mode == 'printer': self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.setEnabled(self.mainapp.checkNPName(unicode(self.entNPName.text()))) if nr == 2: downloadable_selected = False if self.rbtnNPDownloadableDriverSearch.isChecked(): combobox = self.cmbNPDownloadableDriverFoundPrinters iter = combobox.get_active_iter() if iter and combobox.get_model().get_value(iter, 1): downloadable_selected = True if not self.rbtnNPFoomatic.isChecked() and self.filechooserPPD.get_filename(): pass self.btnNPForward.setEnabled(bool(downloadable_selected)) if nr == 3: iter = self.tvNPDrivers.currentItem() self.btnNPForward.setEnabled(bool(iter)) if nr == 4: self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.setEnabled(self.tvNCMembers.count() > 0) if nr == 7: if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1: accepted = self.rbtnNPDownloadLicenseYes.get_active() else: treeview = self.tvNPDownloadableDrivers (model, iter) = treeview.get_selection().get_selected() accepted = iter != None self.btnNPForward.set_sensitive(accepted) def getDevices_thread(self): try: debugprint('Connecting (devices)') cups.setServer(self.mainapp.connect_server) cups.setUser('jr') cups.setPasswordCB(self.mainapp.cupsPasswdCallback) c = cups.Connection() debugprint('Fetching devices') self.devices_result = cupshelpers.getDevices(c) except cups.IPPError: (e, msg) = None self.devices_result = cups.IPPError(e, msg) except: debugprint('Exception in getDevices_thread') self.devices_result = { } try: debugprint('Closing connection (devices)') del c except: pass debugprint('Releasing devices lock') self.devices_lock.release() def queryDevices(self): if not self.devices_lock.acquire(0): debugprint('queryDevices: in progress') return None debugprint('Lock acquired for devices thread') thread.start_new_thread(self.getDevices_thread, ()) debugprint('Devices thread started') def fetchDevices(self, parent = None): debugprint('fetchDevices') self.queryDevices() time.sleep(0.1) waiting = False while self.devices_lock.locked(): if not waiting: waiting = True self.WaitWindow.setText('<b>' + _('Searching') + '</b><br/><br/>' + _('Searching for printers')) self.WaitWindow.show() QApplication.processEvents() time.sleep(0.1) if waiting: self.WaitWindow.hide() debugprint('Got devices') result = self.devices_result if isinstance(result, cups.IPPError): raise result return result def get_hpfax_device_id(self, faxuri): os.environ['URI'] = faxuri cmd = 'LC_ALL=C DISPLAY= hp-info -d "${URI}"' debugprint(faxuri + ': ' + cmd) try: p = subprocess.Popen(cmd, shell = True, stdin = file('/dev/null'), stdout = subprocess.PIPE, stderr = subprocess.PIPE) (stdout, stderr) = p.communicate() except: return None for line in stdout.split('\n'): if line.find('fax-type') == -1: continue faxtype = -1 res = re.search('(\\d+)', line) if res: resg = res.groups() faxtype = resg[0] if faxtype >= 0: break continue if faxtype < 0: return None elif faxtype == 4: return cupshelpers.parseDeviceID('MFG:HP;MDL:Fax 2;DES:HP Fax 2;') else: return cupshelpers.parseDeviceID('MFG:HP;MDL:Fax;DES:HP Fax;') def get_hplip_uri_for_network_printer(self, host, mode): os.environ['HOST'] = host if mode == 'print': mod = '-c' elif mode == 'fax': mod = '-f' else: mod = '-c' cmd = 'hp-makeuri ' + mod + ' "${HOST}"' debugprint(host + ': ' + cmd) uri = None try: p = subprocess.Popen(cmd, shell = True, stdin = file('/dev/null'), stdout = subprocess.PIPE, stderr = subprocess.PIPE) (stdout, stderr) = p.communicate() except: return None uri = stdout.strip() return uri def getNetworkPrinterMakeModel(self, device): host = None s = device.uri.find('://') if s != -1: s += 3 e = device.uri[s:].find(':') if e == -1: e = device.uri[s:].find('/') if e == -1: e = device.uri[s:].find('?') if e == -1: e = len(device.uri) host = device.uri[s:s + e] if host: os.environ['HOST'] = host cmd = '/usr/lib/cups/backend/snmp "${HOST}"' debugprint(host + ': ' + cmd) stdout = None try: p = subprocess.Popen(cmd, shell = True, stdin = file('/dev/null'), stdout = subprocess.PIPE, stderr = subprocess.PIPE) (stdout, stderr) = p.communicate() except: pass if stdout != None: mm = re.sub('^\\s*\\S+\\s+\\S+\\s+"', '', stdout) mm = re.sub('"\\s+.*$', '', mm) if mm and mm != '': device.make_and_model = mm make_and_model = None if len(device.make_and_model) > 7: make_and_model = device.make_and_model elif len(device.info) > 7: make_and_model = device.info make_and_model = re.sub('\\s*(\\(|\\d+\\.\\d+\\.\\d+\\.\\d+).*$', '', make_and_model) if make_and_model and not (device.id): mk = None md = None (mk, md) = cupshelpers.ppds.ppdMakeModelSplit(make_and_model) device.id = 'MFG:' + mk + ';MDL:' + md + ';DES:' + mk + ' ' + md + ';' device.id_dict = cupshelpers.parseDeviceID(device.id) if host: hplipuri = self.get_hplip_uri_for_network_printer(host, 'print') if hplipuri: device.uri = hplipuri s = hplipuri.find('/usb/') if s == -1: s = hplipuri.find('/par/') if s == -1: s = hplipuri.find('/net/') if s != -1: s += 5 e = hplipuri[s:].find('?') if e == -1: e = len(hplipuri) mdl = hplipuri[s:s + e].replace('_', ' ') if mdl.startswith('hp ') or mdl.startswith('HP '): mdl = mdl[3:] device.make_and_model = 'HP ' + mdl device.id = 'MFG:HP;MDL:' + mdl + ';DES:HP ' + mdl + ';' device.id_dict = cupshelpers.parseDeviceID(device.id) return host def fillDeviceTab(self, current_uri = None, query = True): if query: try: devices = self.fetchDevices() except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) devices = { } except: nonfatalException() devices = { } if current_uri: if devices.has_key(current_uri): current = devices.pop(current_uri) else: current = cupshelpers.Device(current_uri) current.info = 'Current device' self.devices = devices.values() for device in self.devices: if device.type == 'usb': ser = None s = device.uri.find('?serial=') if s != -1: s += 8 e = device.uri[s:].find('?') if e == -1: e = len(device.uri) ser = device.uri[s:s + e] mod = None s = device.uri[6:].find('/') if s != -1: s += 7 e = device.uri[s:].find('?') if e == -1: e = len(device.uri) mod = device.uri[s:s + e].lower().replace('%20', '_') if mod.startswith('hp_'): mod = mod[3:] matchfound = 0 for hpdevice in self.devices: hpser = None hpmod = None uri = hpdevice.uri if not uri.startswith('hp:'): continue if ser: s = uri.find('?serial=') if s != -1: s += 8 e = uri[s:].find('?') if e == -1: e = len(uri) hpser = uri[s:s + e] if hpser != ser: continue matchfound = 1 if mod: if ser: pass if not hpser: s = uri.find('/usb/') if s != -1: s += 5 e = uri[s:].find('?') if e == -1: e = len(uri) hpmod = uri[s:s + e].lower() if hpmod.startswith('hp_'): hpmod = hpmod[3:] if hpmod != mod: continue matchfound = 1 if matchfound == 1: break continue if matchfound == 1: device.uri = 'delete' if device.type == 'hal': if device.uri.startswith('hal:///org/freedesktop/Hal/devices/usb_device'): device.uri = 'delete' if device.type == 'socket': device.uri = device.uri.replace(':9100', '') try: if False and device.type in ('socket', 'lpd', 'ipp', 'bluetooth'): host = self.getNetworkPrinterMakeModel(device) faxuri = None if host: faxuri = self.get_hplip_uri_for_network_printer(host, 'fax') if faxuri: self.devices.append(cupshelpers.Device(faxuri, **{ 'device-class': 'direct', 'device-info': device.info + ' HP Fax HPLIP', 'device-device-make-and-model': 'HP Fax', 'device-id': 'MFG:HP;MDL:Fax;DES:HP Fax;' })) if device.uri.startswith('hp:'): device.type = 'hp' device.info += ' HPLIP' continue nonfatalException() continue for i in range(len(self.devices)): for j in range(len(self.devices)): if i == j: continue device1 = self.devices[i] device2 = self.devices[j] if device1.uri == 'delete' or device2.uri == 'delete': continue if device1.uri == device2.uri: if not device1.id: device1.uri = 'delete' elif not device2.id: device2.uri = 'delete' elif len(device1.id) < len(device2.id): device1.uri = 'delete' else: device2.uri = 'delete' device1.id self.devices = filter((lambda x: x.uri not in ('hp:/no_device_found', 'hpfax:/no_device_found', 'hp', 'hpfax', 'hal', 'beh', 'scsi', 'http', 'delete')), self.devices) self.devices.sort() self.devices.append(cupshelpers.Device('', **{ 'device-info': _('Other') })) if current_uri: current.info += _(' (Current)') self.devices.insert(0, current) self.device = current self.tvNPDevices.clear() for device in self.devices: self.tvNPDevices.addItem(device.info) self.tvNPDevices.setCurrentRow(0) self.on_tvNPDevices_cursor_changed() def on_entNPTDevice_changed(self, entry): self.setNPButtons() def on_entSMBURI_changed(self, text): uri = unicode(text) (group, host, share, user, password) = SMBURI(uri = uri).separate() if user: self.entSMBUsername.setText(user) if password: self.entSMBPassword.setText(password) if user or password: uri = SMBURI(group = group, host = host, share = share).get_uri() self.entSMBURI.setText(uri) self.rbtnSMBAuthSet.setChecked(True) elif unicode(self.entSMBUsername.text()) == '': self.rbtnSMBAuthPrompt.setChecked(True) self.btnSMBVerify.setEnabled(bool(uri)) def on_rbtnSMBAuthSet_toggled(self, ticked): self.tblSMBAuth.setEnabled(ticked) def on_entNPTIPPHostname_textChanged(self): valid = len(self.entNPTIPPHostname.text()) > 0 self.btnIPPFindQueue.setEnabled(valid) self.update_IPP_URI_label() def update_IPP_URI_label(self): hostname = unicode(self.entNPTIPPHostname.text()) queue = unicode(self.entNPTIPPQueuename.text()) if len(hostname) > 0: pass valid = queue != '/printers/' if valid: uri = 'ipp://%s%s' % (hostname, queue) self.lblIPPURI.setText(uri) self.lblIPPURI.show() self.entNPTIPPQueuename.show() else: self.lblIPPURI.hide() self.btnIPPVerify.setEnabled(valid) self.setNPButtons() def on_btnIPPFindQueue_clicked(self): self.IPPBrowseBox.clear() host = str(self.entNPTIPPHostname.text()) cups.setServer(host) printers = classes = { } try: c = cups.Connection() printers = c.getPrinters() classes = c.getClasses() del c except RuntimeError: pass except cups.IPP_Error: (e, msg) = None for printer, dict in printers.iteritems(): self.IPPBrowseBox.addItem(printer) for pclass, dict in classes.iteritems(): pass if len(printers) + len(classes) == 0: QMessageBox.information(self, _('No queues'), _('There are no queues available.')) on_btnIPPFindQueue_clicked = pyqtSignature('')(on_btnIPPFindQueue_clicked) def on_tvNPDevices_cursor_changed(self): device = self.devices[self.tvNPDevices.currentRow()] self.device = device self.lblNPDeviceDescription.setText('') page = self.new_printer_device_tabs.get(device.type, 1) self.ntbkNPType.setCurrentIndex(page) type = device.type url = device.uri.split(':', 1)[-1] if page == 0: if device.type == 'parallel': text = _('A printer connected to the parallel port.') elif device.type == 'usb': text = _('A printer connected to a USB port.') elif device.type == 'hp': text = _('HPLIP software driving a printer, or the printer function of a multi-function device.') elif device.type == 'hpfax': text = _('HPLIP software driving a fax machine, or the fax function of a multi-function device.') elif device.type == 'hal': text = _('Local printer detected by the Hardware Abstraction Layer (HAL).') else: text = device.uri self.lblNPDeviceDescription.setText(text) elif device.type == 'socket': if device.uri.startswith('socket'): host = device.uri[9:] i = host.find(':') if i != -1: port = int(host[i + 1:]) host = host[:i] else: port = 9100 self.entNPTDirectJetHostname.setText(host) self.entNPTDirectJetPort.setText(str(port)) elif device.type == 'serial': if not device.is_class: options = device.uri.split('?')[1] options = options.split('+') option_dict = { } for option in options: (name, value) = option.split('=') option_dict[name] = value for widget, name, optionvalues in ((self.cmbNPTSerialBaud, 'baud', None), (self.cmbNPTSerialBits, 'bits', None), (self.cmbNPTSerialParity, 'parity', [ 'none', 'odd', 'even']), (self.cmbNPTSerialFlow, 'flow', [ 'none', 'soft', 'hard', 'hard'])): if option_dict.has_key(name): if optionvalues is None: model = widget.get_model() iter = model.get_iter_first() nr = 0 while iter: value = model.get(iter, 0)[0] if value == option_dict[name]: widget.set_active(nr) break iter = model.iter_next(iter) nr += 1 else: nr = optionvalues.index(option_dict[name]) widget.set_active(nr + 1) optionvalues is None widget.set_active(0) elif device.type in ('ipp', 'http'): if device.uri.startswith('ipp:') or device.uri.startswith('http:'): match = re.match('(ipp|https?)://([^/]+)(.*)', device.uri) if match: server = match.group(2) printer = match.group(3) else: server = '' printer = '' self.entNPTIPPHostname.setText(server) self.entNPTIPPQueuename.setText(printer) self.lblIPPURI.setText(device.uri) self.lblIPPURI.show() self.entNPTIPPQueuename.show() else: self.entNPTIPPHostname.setText('') self.entNPTIPPQueuename.setText('/printers/') self.entNPTIPPQueuename.show() self.lblIPPURI.hide() elif device.type == 'lpd': if device.uri.startswith('lpd'): host = device.uri[6:] i = host.find('/') if i != -1: printer = host[i + 1:] host = host[:i] else: printer = '' self.cmbentNPTLpdHost.addItem(host) self.cmbentNPTLpdQueue.addItem(printer) elif device.uri == 'lpd': pass elif device.uri == 'smb': self.entSMBURI.setText('') self.btnSMBVerify.setEnabled(False) elif device.type == 'smb': self.entSMBUsername.setText('') self.entSMBPassword.setText('') self.entSMBURI.set_text(device.uri[6:]) self.btnSMBVerify.setEnabled(True) else: self.entNPTDevice.setText(device.uri) self.setNPButtons() def getDeviceURI(self): type = self.device.type if type == 'socket': host = unicode(self.entNPTDirectJetHostname.text()) port = unicode(self.entNPTDirectJetPort.text()) device = 'socket://' + host if port: device = device + ':' + port elif type in ('http', 'ipp'): if self.lblIPPURI.isVisible: device = unicode(self.lblIPPURI.text()) else: device = 'ipp' elif type == 'lpd': host = unicode(self.cmbentNPTLpdHost.currentText()) printer = unicode(self.cmbentNPTLpdQueue.currentText()) device = 'lpd://' + host if printer: device = device + '/' + printer elif type == 'parallel': device = self.device.uri elif type == 'scsi': device = '' elif type == 'serial': options = [] for widget, name, optionvalues in ((self.cmbNPTSerialBaud, 'baud', None), (self.cmbNPTSerialBits, 'bits', None), (self.cmbNPTSerialParity, 'parity', ('none', 'odd', 'even')), (self.cmbNPTSerialFlow, 'flow', ('none', 'soft', 'hard', 'hard'))): nr = widget.get_active() if nr: if optionvalues is not None: option = optionvalues[nr - 1] else: option = widget.get_active_text() options.append(name + '=' + option) continue options = '+'.join(options) device = self.device.uri.split('?')[0] if options: device = device + '?' + options elif type == 'smb': uri = unicode(self.entSMBURI.text()) (group, host, share, u, p) = SMBURI(uri = uri).separate() user = '' password = '' if self.rbtnSMBAuthSet.isChecked(): user = unicode(self.entSMBUsername.text()) password = unicode(self.entSMBPassword.text()) uri = SMBURI(group = group, host = host, share = share, user = user, password = password).get_uri() device = 'smb://' + uri elif not self.device.is_class: device = self.device.uri else: device = str(self.entNPTDevice.text()) return device if nr == self.ntbkNewPrinterPages['device']: valid = False try: uri = self.getDeviceURI() valid = validDeviceURI(uri) except: debugprint('exception in getDeviceURI()') self.btnNPForward.setEnabled(valid) self.btnNPBack.hide() else: self.btnNPBack.show() self.btnNPForward.show() self.btnNPApply.hide() if nr == self.ntbkNewPrinterPages['name']: self.btnNPBack.show() if self.dialog_mode == 'printer': self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.setEnabled(self.mainapp.checkNPName(self.entNPName.getText())) if nr == self.ntbkNewPrinterPages['make']: downloadable_selected = False if self.rbtnNPDownloadableDriverSearch.get_active(): combobox = self.cmbNPDownloadableDriverFoundPrinters iter = combobox.get_active_iter() if iter and combobox.get_model().get_value(iter, 1): downloadable_selected = True if not self.rbtnNPFoomatic.get_active() and self.filechooserPPD.get_filename(): pass self.btnNPForward.setEnabled(bool(downloadable_selected)) if nr == self.ntbkNewPrinterPages['model']: (model, iter) = self.tvNPDrivers.get_selection().get_selected() self.btnNPForward.set_sensitive(bool(iter)) if nr == self.ntbkNewPrinterPages['class-members']: self.btnNPForward.hide() self.btnNPApply.show() self.btnNPApply.setEnabled(bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers))) if nr == self.ntbkNewPrinterPages['downloadable']: if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1: accepted = self.rbtnNPDownloadLicenseYes.get_active() else: accepted = True self.btnNPForward.set_sensitive(accepted) def on_rbtnNPFoomatic_toggled(self): rbtn1 = self.rbtnNPFoomatic.isChecked() rbtn2 = self.rbtnNPPPD.isChecked() rbtn3 = self.rbtnNPDownloadableDriverSearch.isChecked() self.tvNPMakes.setEnabled(rbtn1) self.filechooserPPD.setEnabled(rbtn2) for widget in [ self.entNPDownloadableDriverSearch, self.cmbNPDownloadableDriverFoundPrinters]: widget.setEnabled(rbtn3) if rbtn3: pass self.btnNPDownloadableDriverSearch.setEnabled(self.openprinting_query_handle == None) self.setNPButtons() def fillMakeList(self): makes = self.ppds.getMakes() self.tvNPMakes.clear() found = False index = 0 for make in makes: self.tvNPMakes.addItem(make) index = index + 1 if make == self.auto_make: self.tvNPMakes.setCurrentRow(index - 1) found = True continue self.on_tvNPMakes_cursor_changed() def on_tvNPMakes_cursor_changed(self): items = self.tvNPMakes.selectedItems() if len(items) > 0: self.NPMake = unicode(items[0].text()) self.fillModelList() def fillModelList(self): models = self.ppds.getModels(self.NPMake) self.tvNPModels.clear() selected = False index = 0 selected = False for pmodel in models: self.tvNPModels.addItem(pmodel) if self.NPMake == self.auto_make and pmodel == self.auto_model: self.tvNPModels.setCurrentRow(index) selected = True index = index + 1 if not selected: self.tvNPModels.setCurrentRow(0) self.on_tvNPModels_cursor_changed() def fillDriverList(self, pmake, pmodel): self.NPModel = pmodel self.tvNPDrivers.clear() ppds = self.ppds.getInfoFromModel(pmake, pmodel) self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppds.keys()) for i in range(len(self.NPDrivers)): ppd = ppds[self.NPDrivers[i]] driver = ppd['ppd-make-and-model'] driver = driver.replace(' (recommended)', '') try: lpostfix = ' [%s]' % ppd['ppd-natural-language'] driver += lpostfix except KeyError: pass if i == 0: self.tvNPDrivers.addItem(driver + _(' (recommended)')) self.tvNPDrivers.setCurrentRow(0) continue self.tvNPDrivers.addItem(driver) def on_tvNPModels_cursor_changed(self): items = self.tvNPModels.selectedItems() if len(items) > 0: pmodel = unicode(items[0].text()) self.fillDriverList(self.NPMake, pmodel) def getNPPPD(self): try: if self.rbtnNPFoomatic.isChecked(): nr = self.tvNPDrivers.currentRow() ppd = self.NPDrivers[nr] elif self.rbtnNPPPD.isChecked(): ppd = cups.PPD(unicode(self.filechooserPPD.getText())) else: ppd = 'XXX' except RuntimeError: e = None if self.rbtnNPFoomatic.isChecked(): err_title = _('Database error') err_text = _("The '%s' driver cannot be used with printer '%s %s'.") (model, iter) = self.tvNPDrivers.get_selection().get_selected() nr = model.get_path(iter)[0] driver = self.NPDrivers[nr] if driver.startswith('gutenprint'): err = _("You will need to install the '%s' package in order to use this driver.") % 'gutenprint-foomatic' else: err = err_text % (driver, self.NPMake, self.NPModel) elif self.rbtnNPPPD.isChecked(): err_title = _('PPD error') filename = self.filechooserPPD.get_filename() err = _('Failed to read PPD file. Possible reason follows:') + '\n' os.environ['PPD'] = filename p = os.popen('/usr/bin/cupstestppd -rvv "$PPD"', 'r') output = p.readlines() p.close() err += reduce((lambda x, y: x + y), output) else: err_title = _('Downloadable drivers') err_text = _('Support for downloadable drivers is not yet completed.') error_text = '<span weight="bold" size="larger">' + err_title + '</span>\n\n' + err QMessageBox.critical(self, err_title, error_text) return None if isinstance(ppd, str) or isinstance(ppd, unicode): try: if ppd != 'raw': f = self.mainapp.cups.getServerPPD(ppd) ppd = cups.PPD(f) os.unlink(f) except AttributeError: nonfatalException() debugprint('pycups function getServerPPD not available: never mind') except RuntimeError: nonfatalException() debugprint('libcups from CUPS 1.3 not available: never mind') except cups.IPPError: nonfatalException() debugprint('CUPS 1.3 server not available: never mind') except: None<EXCEPTION MATCH>AttributeError None<EXCEPTION MATCH>AttributeError return ppd def on_btnNPApply_clicked(self): if self.dialog_mode in ('class', 'printer'): name = unicode(self.entNPName.text()) location = unicode(self.entNPLocation.text()) info = unicode(self.entNPDescription.text()) else: name = self.mainapp.printer.name check = False checkppd = None ppd = self.ppd if self.dialog_mode == 'class': members = self.mainapp.getCurrentClassMembers(self.tvNCMembers) try: for member in members: self.passwd_retry = False self.mainapp.cups.addPrinterToClass(str(member), name) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None except: None<EXCEPTION MATCH>cups.IPPError None<EXCEPTION MATCH>cups.IPPError if self.dialog_mode == 'printer': self.device.uri = unicode(self.device.uri) uri = None if self.device.uri: uri = self.device.uri else: uri = self.getDeviceURI() if not self.ppd: self.nextNPTab(-1) return None for option in self.options.itervalues(): option.writeback() self.WaitWindow.setText('<b>' + _('Adding') + '</b><br /><br />' + _('Adding printer')) self.WaitWindow.show() QApplication.processEvents() try: self.passwd_retry = False if isinstance(ppd, str) or isinstance(ppd, unicode): self.mainapp.cups.addPrinter(name, ppdname = ppd, device = uri, info = info, location = location) check = True elif ppd is None: self.mainapp.cups.addPrinter(name, device = uri, info = info, location = location) else: cupshelpers.setPPDPageSize(ppd, self.language[0]) self.mainapp.cups.addPrinter(name, ppd = ppd, device = uri, info = info, location = location) check = True checkppd = ppd cupshelpers.activateNewPrinter(self.mainapp.cups, name) except cups.IPPError: (e, msg) = None self.WaitWindow.hide() self.show_IPP_Error(e, msg) return None except: self.WaitWindow.hide() fatalException(1) self.WaitWindow.hide() if self.dialog_mode in ('class', 'printer'): try: self.passwd_retry = False self.mainapp.cups.setPrinterLocation(name, location) self.passwd_retry = False self.mainapp.cups.setPrinterInfo(name, info) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None except: None<EXCEPTION MATCH>cups.IPPError None<EXCEPTION MATCH>cups.IPPError if self.dialog_mode == 'device': try: uri = self.getDeviceURI() self.mainapp.cups.addPrinter(name, device = uri) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None except: None<EXCEPTION MATCH>cups.IPPError None<EXCEPTION MATCH>cups.IPPError if self.dialog_mode == 'ppd': if not ppd: ppd = self.ppd = self.getNPPPD() if not ppd: self.nextNPTab(-1) return None raw = False if isinstance(ppd, str) or isinstance(ppd, unicode): if self.rbtnChangePPDasIs.isChecked(): try: self.mainapp.cups.addPrinter(name, ppdname = 'raw') except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) except: None<EXCEPTION MATCH>cups.IPPError None<EXCEPTION MATCH>cups.IPPError try: self.mainapp.cups.addPrinter(name, ppdname = ppd) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) return None try: filename = self.mainapp.cups.getPPD(name) ppd = cups.PPD(filename) os.unlink(filename) except cups.IPPError: (e, msg) = None if e == cups.IPP_NOT_FOUND: raw = True else: self.show_IPP_Error(e, msg) return None except: e == cups.IPP_NOT_FOUND None<EXCEPTION MATCH>cups.IPPError if not self.rbtnChangePPDasIs.isChecked(): cupshelpers.copyPPDOptions(self.mainapp.ppd, ppd) else: for option in self.options.itervalues(): option.writeback() cupshelpers.setPPDPageSize(ppd, self.language[0]) try: self.mainapp.cups.addPrinter(name, ppd = ppd) except cups.IPPError: (e, msg) = None self.show_IPP_Error(e, msg) if not raw: check = True checkppd = ppd self.accept() self.mainapp.populateList(start_printer = name) if check: try: self.checkDriverExists(name, ppd = checkppd) nonfatalException() on_btnNPApply_clicked = pyqtSignature('')(on_btnNPApply_clicked) def checkDriverExists(self, name, ppd = None): '''Check that the driver for an existing queue actually exists, and prompt to install the appropriate package if not. ppd: cups.PPD object, if already created''' server = cups.getServer() if not server == 'localhost' and server == '127.0.0.1' and server == '::1' or server[0] == '/': return None if not ppd: try: filename = self.mainapp.cups.getPPD(name) except cups.IPPError: (e, msg) = None if e == cups.IPP_NOT_FOUND: return None else: self.show_IPP_Error(e, msg) return None except: e == cups.IPP_NOT_FOUND ppd = cups.PPD(filename) os.unlink(filename) (pkgs, exes) = cupshelpers.missingPackagesAndExecutables(ppd) if len(pkgs) > 0 or len(exes) > 0: install = '/usr/bin/system-install-packages' if len(pkgs) > 0 and os.access(install, os.X_OK): pkg = pkgs[0] install_text = '<span weight="bold" size="larger">' + _('Install driver') + '</span>\n\n' + _("Printer '%s' requires the %s package but it is not currently installed.") % (name, pkg) dialog = self.InstallDialog self.lblInstall.set_markup(install_text) else: error_text = '<span weight="bold" size="larger">' + _('Missing driver') + '</span>\n\n' + _("Printer '%s' requires the '%s' program but it is not currently installed. Please install it before using this printer.") % (name, exes + pkgs[0]) QMessageBox.error(self, '', error_text) def on_entNPTIPPQueuename_textChanged(self, ent): self.update_IPP_URI_label() def on_IPPBrowseBox_currentTextChanged(self, text): self.update_IPP_URI_label() def on_btnNPCancel_clicked(self): self.hide() on_btnNPCancel_clicked = pyqtSignature('')(on_btnNPCancel_clicked) if __name__ == '__main__': app = QApplication(sys.argv) applet = GUI() sys.exit(app.exec_())